package com.apigen.platform.service.impl;

import com.apigen.platform.dto.CodeGenerationRequest;
import com.apigen.platform.entity.GenTable;
import com.apigen.platform.entity.GenTableField;
import com.apigen.platform.mapper.GenTableMapper;
import com.apigen.platform.mapper.GenTableFieldMapper;
import com.apigen.platform.service.EnhancedCodeGenerationService;
import com.apigen.platform.service.TemplateRenderService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 增强版代码生成服务实现类
 *
 * @author API Generator Platform
 * @since 2025-10-03
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class EnhancedCodeGenerationServiceImpl implements EnhancedCodeGenerationService {

    private final GenTableMapper genTableMapper;
    private final GenTableFieldMapper genTableFieldMapper;
    private final TemplateRenderService templateRenderService;

    // 临时文件存储
    private final Map<String, String> tempFileStorage = new ConcurrentHashMap<>();

    @Override
    public Map<String, Object> generateProject(CodeGenerationRequest request) {
        String generationId = generateId();
        
        try {
            log.info("开始生成项目: {}, ID: {}", request.getProjectName(), generationId);
            
            // 创建临时目录
            Path tempDir = createTempDirectory(generationId);
            
            // 生成项目结构
            generateProjectStructure(request, tempDir);
            
            // 打包项目
            String zipFilePath = createProjectZip(tempDir, generationId);
            
            // 存储文件路径
            tempFileStorage.put(generationId, zipFilePath);
            
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("generationId", generationId);
            result.put("projectName", request.getProjectName());
            result.put("message", "项目生成成功");
            result.put("downloadUrl", "/api/code-generation/download/" + generationId);
            result.put("createTime", LocalDateTime.now());
            
            log.info("项目生成完成: {}", generationId);
            return result;
            
        } catch (Exception e) {
            log.error("生成项目失败: {}", e.getMessage(), e);
            
            Map<String, Object> result = new HashMap<>();
            result.put("success", false);
            result.put("message", "生成项目失败: " + e.getMessage());
            return result;
        }
    }

    @Override
    public Map<String, Object> previewCode(CodeGenerationRequest request) {
        try {
            log.info("开始预览代码: {}", request.getProjectName());
            
            Map<String, String> fileContents = new HashMap<>();
            
            // 生成项目级别文件
            generateProjectFiles(request, fileContents);
            
            // 为每个表生成代码
            for (Long tableId : request.getTableIds()) {
                generateTableFiles(request, tableId, fileContents);
            }
            
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("files", fileContents);
            result.put("fileCount", fileContents.size());
            
            log.info("代码预览完成，共生成 {} 个文件", fileContents.size());
            return result;
            
        } catch (Exception e) {
            log.error("预览代码失败: {}", e.getMessage(), e);
            
            Map<String, Object> result = new HashMap<>();
            result.put("success", false);
            result.put("message", "预览代码失败: " + e.getMessage());
            return result;
        }
    }

    @Override
    public Map<String, Object> generateTableCode(CodeGenerationRequest request, Long tableId) {
        try {
            log.info("开始生成表代码: tableId={}", tableId);
            
            Map<String, String> fileContents = new HashMap<>();
            generateTableFiles(request, tableId, fileContents);
            
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("files", fileContents);
            result.put("tableId", tableId);
            
            return result;
            
        } catch (Exception e) {
            log.error("生成表代码失败: {}", e.getMessage(), e);
            
            Map<String, Object> result = new HashMap<>();
            result.put("success", false);
            result.put("message", "生成表代码失败: " + e.getMessage());
            return result;
        }
    }

    @Override
    public Map<String, Object> downloadProject(String generationId) {
        try {
            String filePath = tempFileStorage.get(generationId);
            if (filePath == null) {
                throw new RuntimeException("生成记录不存在或已过期");
            }
            
            File file = new File(filePath);
            if (!file.exists()) {
                throw new RuntimeException("生成的文件不存在");
            }
            
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("filePath", filePath);
            result.put("fileName", file.getName());
            result.put("fileSize", file.length());
            
            return result;
            
        } catch (Exception e) {
            log.error("获取下载文件失败: {}", e.getMessage(), e);
            
            Map<String, Object> result = new HashMap<>();
            result.put("success", false);
            result.put("message", "获取下载文件失败: " + e.getMessage());
            return result;
        }
    }

    @Override
    public Boolean cleanupTempFiles(String generationId) {
        try {
            String filePath = tempFileStorage.remove(generationId);
            if (filePath != null) {
                File file = new File(filePath);
                if (file.exists()) {
                    file.delete();
                }
                
                // 删除临时目录
                Path tempDir = Paths.get(System.getProperty("java.io.tmpdir"), "code-gen-" + generationId);
                if (Files.exists(tempDir)) {
                    deleteDirectory(tempDir.toFile());
                }
            }
            
            log.info("清理临时文件成功: {}", generationId);
            return true;
            
        } catch (Exception e) {
            log.error("清理临时文件失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 生成项目结构
     */
    private void generateProjectStructure(CodeGenerationRequest request, Path tempDir) throws IOException {
        // 创建基础目录结构
        createDirectoryStructure(request, tempDir);
        
        // 生成项目文件
        Map<String, String> fileContents = new HashMap<>();
        generateProjectFiles(request, fileContents);
        
        // 为每个表生成代码
        for (Long tableId : request.getTableIds()) {
            generateTableFiles(request, tableId, fileContents);
        }
        
        // 写入文件
        writeFilesToDirectory(fileContents, tempDir);
    }

    /**
     * 创建目录结构
     */
    private void createDirectoryStructure(CodeGenerationRequest request, Path tempDir) throws IOException {
        String basePackagePath = request.getPackageName().replace(".", "/");
        
        // Java源码目录
        Path javaDir = tempDir.resolve("src/main/java/" + basePackagePath);
        Files.createDirectories(javaDir.resolve("controller"));
        Files.createDirectories(javaDir.resolve("service"));
        Files.createDirectories(javaDir.resolve("service/impl"));
        Files.createDirectories(javaDir.resolve("mapper"));
        Files.createDirectories(javaDir.resolve("entity"));
        Files.createDirectories(javaDir.resolve("dto"));
        Files.createDirectories(javaDir.resolve("vo"));
        Files.createDirectories(javaDir.resolve("common"));
        Files.createDirectories(javaDir.resolve("config"));
        
        // 资源目录
        Path resourcesDir = tempDir.resolve("src/main/resources");
        Files.createDirectories(resourcesDir.resolve("mapper"));
        
        // 测试目录
        Path testDir = tempDir.resolve("src/test/java/" + basePackagePath);
        Files.createDirectories(testDir);
    }

    /**
     * 生成项目级别文件
     */
    private void generateProjectFiles(CodeGenerationRequest request, Map<String, String> fileContents) {
        Map<String, Object> projectData = templateRenderService.generateProjectData(request);
        
        // POM文件
        String pomContent = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "pom.xml.hbs"), 
                projectData);
        fileContents.put("pom.xml", pomContent);
        
        // 配置文件
        String appYmlContent = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "application.yml.hbs"), 
                projectData);
        fileContents.put("src/main/resources/application.yml", appYmlContent);
        
        // 主启动类
        String mainClassContent = generateMainClass(request, projectData);
        String mainClassPath = String.format("src/main/java/%s/%sApplication.java", 
                request.getPackageName().replace(".", "/"),
                toPascalCase(request.getProjectName()));
        fileContents.put(mainClassPath, mainClassContent);
        
        // 通用类
        generateCommonFiles(request, projectData, fileContents);
        
        // Knife4j配置
        String knife4jContent = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "knife4j-config.hbs"), 
                projectData);
        String knife4jPath = String.format("src/main/java/%s/config/Knife4jConfiguration.java", 
                request.getPackageName().replace(".", "/"));
        fileContents.put(knife4jPath, knife4jContent);
        
        // README文件
        fileContents.put("README.md", generateReadme(request));
        
        // .gitignore文件
        fileContents.put(".gitignore", generateGitignore());
    }

    /**
     * 生成表相关文件
     */
    private void generateTableFiles(CodeGenerationRequest request, Long tableId, Map<String, String> fileContents) {
        // 获取表信息
        GenTable table = genTableMapper.selectById(tableId);
        if (table == null) {
            throw new RuntimeException("表不存在: " + tableId);
        }
        
        // 获取字段信息
        List<GenTableField> fields = genTableFieldMapper.selectList(
                new LambdaQueryWrapper<GenTableField>().eq(GenTableField::getTableId, tableId));
        
        // 生成模板数据
        Map<String, Object> tableData = templateRenderService.generateTableData(table, fields, request);
        
        String entityName = toPascalCase(table.getTableName());
        String packagePath = request.getPackageName().replace(".", "/");
        
        // 生成各种文件
        generateEntityFile(entityName, packagePath, tableData, fileContents);
        generateDtoFile(entityName, packagePath, tableData, fileContents);
        generateVoFile(entityName, packagePath, tableData, fileContents);
        generateMapperFile(entityName, packagePath, tableData, fileContents);
        generateMapperXmlFile(entityName, tableData, fileContents);
        generateServiceFile(entityName, packagePath, tableData, fileContents);
        generateServiceImplFile(entityName, packagePath, tableData, fileContents);
        generateControllerFile(entityName, packagePath, tableData, fileContents);
    }

    /**
     * 生成实体文件
     */
    private void generateEntityFile(String entityName, String packagePath, Map<String, Object> tableData, Map<String, String> fileContents) {
        String content = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "entity.hbs"), 
                tableData);
        String filePath = String.format("src/main/java/%s/entity/%s.java", packagePath, entityName);
        fileContents.put(filePath, content);
    }

    /**
     * 生成DTO文件
     */
    private void generateDtoFile(String entityName, String packagePath, Map<String, Object> tableData, Map<String, String> fileContents) {
        String content = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "dto.hbs"), 
                tableData);
        String filePath = String.format("src/main/java/%s/dto/%sDTO.java", packagePath, entityName);
        fileContents.put(filePath, content);
    }

    /**
     * 生成VO文件
     */
    private void generateVoFile(String entityName, String packagePath, Map<String, Object> tableData, Map<String, String> fileContents) {
        String content = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "vo.hbs"), 
                tableData);
        String filePath = String.format("src/main/java/%s/vo/%sVO.java", packagePath, entityName);
        fileContents.put(filePath, content);
    }

    /**
     * 生成Mapper文件
     */
    private void generateMapperFile(String entityName, String packagePath, Map<String, Object> tableData, Map<String, String> fileContents) {
        String content = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "mapper.hbs"), 
                tableData);
        String filePath = String.format("src/main/java/%s/mapper/%sMapper.java", packagePath, entityName);
        fileContents.put(filePath, content);
    }

    /**
     * 生成Mapper XML文件
     */
    private void generateMapperXmlFile(String entityName, Map<String, Object> tableData, Map<String, String> fileContents) {
        String content = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "mapper.xml.hbs"), 
                tableData);
        String filePath = String.format("src/main/resources/mapper/%sMapper.xml", entityName);
        fileContents.put(filePath, content);
    }

    /**
     * 生成Service文件
     */
    private void generateServiceFile(String entityName, String packagePath, Map<String, Object> tableData, Map<String, String> fileContents) {
        String content = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "service.hbs"), 
                tableData);
        String filePath = String.format("src/main/java/%s/service/%sService.java", packagePath, entityName);
        fileContents.put(filePath, content);
    }

    /**
     * 生成Service实现文件
     */
    private void generateServiceImplFile(String entityName, String packagePath, Map<String, Object> tableData, Map<String, String> fileContents) {
        String content = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "service-impl.hbs"), 
                tableData);
        String filePath = String.format("src/main/java/%s/service/impl/%sServiceImpl.java", packagePath, entityName);
        fileContents.put(filePath, content);
    }

    /**
     * 生成Controller文件
     */
    private void generateControllerFile(String entityName, String packagePath, Map<String, Object> tableData, Map<String, String> fileContents) {
        String content = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "controller.hbs"), 
                tableData);
        String filePath = String.format("src/main/java/%s/controller/%sController.java", packagePath, entityName);
        fileContents.put(filePath, content);
    }

    /**
     * 生成通用文件
     */
    private void generateCommonFiles(CodeGenerationRequest request, Map<String, Object> projectData, Map<String, String> fileContents) {
        String packagePath = request.getPackageName().replace(".", "/");
        
        // Result类
        String resultContent = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "result.hbs"), 
                projectData);
        fileContents.put(String.format("src/main/java/%s/common/Result.java", packagePath), resultContent);
        
        // PageResult类
        String pageResultContent = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "page-result.hbs"), 
                projectData);
        fileContents.put(String.format("src/main/java/%s/common/PageResult.java", packagePath), pageResultContent);
        
        // ErrorCode类
        String errorCodeContent = templateRenderService.renderTemplate(
                templateRenderService.getTemplateContent("spring-boot", "error-code.hbs"), 
                projectData);
        fileContents.put(String.format("src/main/java/%s/common/ErrorCode.java", packagePath), errorCodeContent);
    }

    /**
     * 生成主启动类
     */
    private String generateMainClass(CodeGenerationRequest request, Map<String, Object> projectData) {
        String className = toPascalCase(request.getProjectName()) + "Application";
        
        return String.format(
                "package %s;\n\n" +
                "import org.mybatis.spring.annotation.MapperScan;\n" +
                "import org.springframework.boot.SpringApplication;\n" +
                "import org.springframework.boot.autoconfigure.SpringBootApplication;\n\n" +
                "/**\n" +
                " * %s 启动类\n" +
                " *\n" +
                " * @author %s\n" +
                " * @since %s\n" +
                " */\n" +
                "@SpringBootApplication\n" +
                "@MapperScan(\"%s.mapper\")\n" +
                "public class %s {\n\n" +
                "    public static void main(String[] args) {\n" +
                "        SpringApplication.run(%s.class, args);\n" +
                "        System.out.println(\"🚀 %s 启动成功！\");\n" +
                "        System.out.println(\"📖 接口文档地址: http://localhost:8080/doc.html\");\n" +
                "    }\n" +
                "}",
                request.getPackageName(),
                request.getProjectName(),
                request.getAuthor(),
                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
                request.getPackageName(),
                className,
                className,
                request.getProjectName()
        );
    }

    /**
     * 生成README文件
     */
    private String generateReadme(CodeGenerationRequest request) {
        return String.format(
                "# %s\n\n" +
                "%s\n\n" +
                "## 技术栈\n\n" +
                "- Spring Boot 2.7.18\n" +
                "- MyBatis Plus 3.5.3\n" +
                "- Knife4j 4.4.0 (API文档)\n" +
                "- MySQL 8.0\n" +
                "- Druid 连接池\n\n" +
                "## 快速开始\n\n" +
                "### 1. 环境要求\n\n" +
                "- JDK 8+\n" +
                "- Maven 3.6+\n" +
                "- MySQL 8.0+\n\n" +
                "### 2. 运行项目\n\n" +
                "```bash\n" +
                "mvn spring-boot:run\n" +
                "```\n\n" +
                "### 3. 访问接口文档\n\n" +
                "启动成功后，访问：http://localhost:8080/doc.html\n\n" +
                "## 作者\n\n" +
                "%s\n",
                request.getProjectName(),
                StringUtils.hasText(request.getDescription()) ? request.getDescription() : "基于Spring Boot的API项目",
                request.getAuthor()
        );
    }

    /**
     * 生成.gitignore文件
     */
    private String generateGitignore() {
        return "# Compiled class file\n" +
                "*.class\n\n" +
                "# Log file\n" +
                "*.log\n\n" +
                "# Package Files\n" +
                "*.jar\n" +
                "*.war\n" +
                "*.nar\n" +
                "*.ear\n" +
                "*.zip\n" +
                "*.tar.gz\n" +
                "*.rar\n\n" +
                "# Maven\n" +
                "target/\n" +
                "pom.xml.tag\n" +
                "pom.xml.releaseBackup\n" +
                "pom.xml.versionsBackup\n" +
                "pom.xml.next\n" +
                "release.properties\n" +
                "dependency-reduced-pom.xml\n" +
                "buildNumber.properties\n" +
                ".mvn/timing.properties\n" +
                ".mvn/wrapper/maven-wrapper.jar\n\n" +
                "# IDE\n" +
                ".idea/\n" +
                "*.iws\n" +
                "*.iml\n" +
                "*.ipr\n" +
                ".vscode/\n" +
                ".settings/\n" +
                ".project\n" +
                ".classpath\n\n" +
                "# OS\n" +
                ".DS_Store\n" +
                "Thumbs.db\n\n" +
                "# Application\n" +
                "logs/\n" +
                "temp/\n";
    }

    /**
     * 将文件内容写入目录
     */
    private void writeFilesToDirectory(Map<String, String> fileContents, Path tempDir) throws IOException {
        for (Map.Entry<String, String> entry : fileContents.entrySet()) {
            Path filePath = tempDir.resolve(entry.getKey());
            Files.createDirectories(filePath.getParent());
            Files.write(filePath, entry.getValue().getBytes(StandardCharsets.UTF_8));
        }
    }

    /**
     * 创建项目ZIP包
     */
    private String createProjectZip(Path tempDir, String generationId) throws IOException {
        String zipFileName = "project-" + generationId + ".zip";
        Path zipFilePath = Paths.get(System.getProperty("java.io.tmpdir"), zipFileName);
        
        try (FileOutputStream fos = new FileOutputStream(zipFilePath.toFile());
             ZipArchiveOutputStream zos = new ZipArchiveOutputStream(fos)) {
            
            Files.walk(tempDir)
                    .filter(path -> !Files.isDirectory(path))
                    .forEach(path -> {
                        try {
                            String entryName = tempDir.relativize(path).toString().replace("\\", "/");
                            ZipArchiveEntry entry = new ZipArchiveEntry(entryName);
                            entry.setSize(Files.size(path));
                            zos.putArchiveEntry(entry);
                            Files.copy(path, zos);
                            zos.closeArchiveEntry();
                        } catch (IOException e) {
                            throw new RuntimeException("创建ZIP文件失败", e);
                        }
                    });
        }
        
        return zipFilePath.toString();
    }

    /**
     * 创建临时目录
     */
    private Path createTempDirectory(String generationId) throws IOException {
        Path tempDir = Paths.get(System.getProperty("java.io.tmpdir"), "code-gen-" + generationId);
        Files.createDirectories(tempDir);
        return tempDir;
    }

    /**
     * 生成唯一ID
     */
    private String generateId() {
        return "gen_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 1000);
    }

    /**
     * 转换为PascalCase
     */
    private String toPascalCase(String str) {
        return templateRenderService.toPascalCase(str);
    }

    /**
     * 删除目录
     */
    private void deleteDirectory(File directory) {
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        deleteDirectory(file);
                    } else {
                        file.delete();
                    }
                }
            }
            directory.delete();
        }
    }
}
